#include <el.h>
#include <bolt.h>
#include "scps.h"
#include "scpstp.h"
#include "tp_sched.h"
#include "tp.h"
#include "tp_debug.h"
#include "ll.h"
#include "pktio.h"
#include "netnode.h"
#include "gw_event.h"
#include "gw.h"
#include "pkt_queue.h"
#include "pkt_pool.h"
#include "l2.h"

static void print_packet(uint8_t *data, int len)
{
  int     i;
  printf("================================\n");
  for (i = 0; i < len; i++) {
    printf ("%02x ", data[i]);
    if (((i + 1) % 16) == 0)
      printf ("\n");
  }
  printf ("\n");
}


void tap_a_in_event(void *userdata)
{
  scps_np_rqts rqts_in;
  int  offset;
  struct llbuf *llbuf = gwio_rx(divert_gwio, divert_gwio->tap_a_fd);
  if (llbuf != NULL) {
    if (debug_gw)
      print_packet(llbuf->data, llbuf->size);
    int cc = nl_ind(&rqts_in, MAX_MTU, &offset);
    printf ("nl ind cc = %d\n", cc);
    
  }
}

void tap_b_in_event(void *userdata)
{
  scps_np_rqts rqts_in;
  int  offset;
  struct llbuf *llbuf = gwio_rx(divert_gwio, divert_gwio->tap_b_fd);
  if (llbuf != NULL) {
    print_packet(llbuf->data, llbuf->size);
    int cc = nl_ind(&rqts_in, MAX_MTU, &offset);
    GW_LOG ("nl ind cc = %d\n", cc);
  }
}

static void lan_rx_event(void *userdata)
{
  struct gw *gw = (struct gw *)userdata;
  do {
    struct llbuf *ll = pkt_event_recv(gw->lan_rxq);
    if (!ll)
      break;
    if (debug_gw)
      llbuf_print(ll);
    ll->in = gw->lan_rxq;
    ll->out = gw->wan_txq;
    if (!l2_rx(ll))
      pkt_pool_free(ll->pool, ll);
  } while (1);
}

static void lan_tx_event(void *userdata)
{
  struct gw *gw = (struct gw *)userdata;
  do {
    struct llbuf *ll = pkt_event_recv(gw->lan_txq);
    if (!ll)
      break;
    GW_LOG("lan tx event\n");
    if (!netnode_send(gw->lan, ll))
      pkt_pool_free(ll->pool, ll);
  } while (1);
}

static void wan_rx_event(void *userdata)
{
  struct gw *gw = (struct gw *)userdata;
  do {
    struct llbuf *ll = pkt_event_recv(gw->wan_rxq);
    if (!ll)
      break;
    if (debug_gw)
      llbuf_print(ll);
    ll->in = gw->wan_rxq;
    ll->out = gw->lan_txq;
    if (!l2_rx(ll))
      pkt_pool_free(ll->pool, ll);
  } while (1);
}


static void wan_tx_event(void *userdata)
{
  struct gw *gw = (struct gw *)userdata;
  do {
    struct llbuf *ll = pkt_event_recv(gw->wan_txq);
    if (!ll)
      break;
    if (!netnode_send(gw->wan, ll))
      pkt_pool_free(ll->pool, ll);
  } while (1);
}


static bool gw_set_lan_rx(int gw_id, int queue)
{
  struct el_events event = {   
    .in_event =  lan_rx_event,
    .out_event = NULL,
  };
  struct gw *gw = gw_get(gw_id);
  if (gw == NULL  || gw->lan_rxq >= 0){
    return false;
  }
  if (pkt_queue_bind(queue, &event, gw)){
    gw->lan_rxq = queue;
    return true;
  } else 
    return false;
}


static bool gw_set_lan_tx(int gw_id, int queue)
{
  struct el_events event = {   
    .in_event =  lan_tx_event,
    .out_event = NULL,
  };
  struct gw *gw = gw_get(gw_id);
  if (gw == NULL  || gw->lan_txq >= 0)
    return false;
  if (pkt_queue_bind(queue, &event, gw)){
    gw->lan_txq = queue;
    return true;
  } else 
    return false; 
}

static bool gw_set_wan_rx(int gw_id, int queue)
{
  struct el_events event = {   
    .in_event =  wan_rx_event,
    .out_event = NULL,
  };
  struct gw *gw = gw_get(gw_id);
  if (gw == NULL  || gw->wan_rxq >= 0)
    return false;
  if (pkt_queue_bind(queue, &event, gw)){
    gw->wan_rxq = queue;
    return true;
  } else 
    return false; 
}

static bool gw_set_wan_tx(int gw_id, int queue)
{
  struct el_events event = {   
    .in_event =  wan_tx_event,
    .out_event = NULL,
  };
  struct gw *gw = gw_get(gw_id);
  if (gw == NULL  || gw->wan_txq >= 0)
    return false;
  if (pkt_queue_bind(queue, &event, gw)){
    gw->wan_txq = queue;
    return true;
  } else 
    return false; 
}

BOLT(gw_queue_lan_rx, "gw <id> queue <queue> lan-rx")
{
  uint32_t gw, lan_rx;
  const char *argz = pop_argz();
  if (!str2u32(argz, &lan_rx)) {
    printf("invalid queue\n");
    return;
  }
  argz = pop_argz();
  if (!str2u32(argz, &gw)) {
    printf("invalid gw\n");
    return;
  }
  if (gw_set_lan_rx(gw, lan_rx))
    printf("success\n");
  else
    printf("failure\n");
}

BOLT(gw_queue_lan_tx, "gw <id> queue <queue> lan-tx")
{
  uint32_t gw, lan_tx;
  const char *argz = pop_argz();
  if (!str2u32(argz, &lan_tx)) {
    printf("invalid queue\n");
    return;
  }
  argz = pop_argz();
  if (!str2u32(argz, &gw)) {
    printf("invalid gw\n");
    return;
  }
  if (gw_set_lan_tx(gw, lan_tx))
    printf("success\n");
  else
    printf("failure\n");
}


BOLT(gw_queue_wan_rx, "gw <id> queue <queue> wan-rx")
{
  uint32_t gw, wan_rx;
  const char *argz = pop_argz();
  if (!str2u32(argz, &wan_rx)) {
    printf("invalid queue\n");
    return;
  }
  argz = pop_argz();
  if (!str2u32(argz, &gw)) {
    printf("invalid gw\n");
    return;
  }
  if (gw_set_wan_rx(gw, wan_rx))
    printf("success\n");
  else
    printf("failure\n");
}


BOLT(gw_queue_wan_tx, "gw <id> queue <queue> wan-tx")
{
  uint32_t gw, wan_tx;
  const char *argz = pop_argz();
  if (!str2u32(argz, &wan_tx)) {
    printf("invalid queue\n");
    return;
  }
  argz = pop_argz();
  if (!str2u32(argz, &gw)) {
    printf("invalid gw\n");
    return;
  }
  if (gw_set_wan_tx(gw, wan_tx))
    printf("success\n");
  else
    printf("failure\n");
}



